package org.rfs.ipodencoder;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.trolltech.qt.QThread;
import com.trolltech.qt.gui.QMessageBox;
import com.trolltech.qt.gui.QMessageBox.StandardButton;

public class IPodEncoderController {
	private IPodEncoderMainView iPodEncoderView;
	private HashMap<String, String> encodingOptions;
	private HashMap<String, HashMap<String, String>> mVideoInfo = new HashMap<String, HashMap<String,String>>();
	
//	private ArrayList<String> lFileNames = null;
	
	private final String encodeCommandSource = "@FFMPEG_BINARY@ -i @INPUTFILE@ -vcodec mpeg4 -flags +aic+qprd+mv0+mv4 -mbd 2 -cmp 2 -subcmp 2 -g 250 -maxrate 2.5M -bufsize 2M -async 1 -acodec libfaac -y";
	private final String mplayerCommandSource = "@MPLAYER_BINARY@ -identify -vo null -frames 0 @INPUTFILE@";
	
	private final HashMap<String, String> mWinBinaries = new HashMap<String, String>(){
		/**
		 * 
		 */
		private static final long serialVersionUID = 5931729533682560923L;
		{
			put("@FFMPEG_BINARY@", "ffmpeg.exe");
			put("@MPLAYER_BINARY@", "mplayer.exe");
		}
	};
	private final HashMap<String, String> mMacBinaries = new HashMap<String, String>(){
		/**
		 * 
		 */
		private static final long serialVersionUID = -4126559893103061517L;
		{
			put("@FFMPEG_BINARY@", "ffmpeg");
			put("@MPLAYER_BINARY@", "mplayer");
		}
	};
	
	private IPodEncoderFFMpegEncodingProgressView fepv = null;
	private IPodEncoderVideoInfoProgressView vipv = null;
	
	public IPodEncoderController(IPodEncoderMainView iPodEncoderView) {
		this.iPodEncoderView = iPodEncoderView;
		encodingOptions = new HashMap<String, String>();
	}

	public HashMap<String, HashMap<String,String>> getInfoForFiles(){
		return mVideoInfo;
	}
	
	public Object removeInputFiles(String sInputFile) {
		return mVideoInfo.remove(sInputFile);
	}
	
	public void setEncodingOptions() {
		//TODO: what if one of those values would be NULL ????
		encodingOptions.put("abitrate", iPodEncoderView.ui.lineEdit_abitrate.text() + "k");
		encodingOptions.put("achannels", iPodEncoderView.ui.lineEdit_achannels.text());
		encodingOptions.put("vbitrate", iPodEncoderView.ui.lineEdit_vbitrate.text() + "k");
		encodingOptions.put("vframerate", iPodEncoderView.ui.lineEdit_vframerate.text());
		encodingOptions.put("vscale", iPodEncoderView.ui.lineEdit_vscale.text());
		
		encodingOptions.put("threads", String.valueOf(iPodEncoderView.ui.comboBox_threads.currentIndex()+1));
		//encodingOptions.put("mode", String.valueOf(iPodEncoderView.ui.comboBox_mode.currentIndex()+1));
	}
	
	private String buildFFMpegExecCommand(String sInputFile, int nMode) {
		String sBinary = encodeCommandSource.replace("@FFMPEG_BINARY@", getBinary("@FFMPEG_BINARY@"));
		StringBuffer sbuf = new StringBuffer(sBinary);
		
		if (encodingOptions.get("vbitrate").length() > 0)
			sbuf.append(" -b " + encodingOptions.get("vbitrate"));
		if (encodingOptions.get("vframerate").length() > 0)
			sbuf.append(" -r " + encodingOptions.get("vframerate"));
		if (encodingOptions.get("vscale").length() > 0)
			sbuf.append(" -s " + encodingOptions.get("vscale"));
		if (encodingOptions.get("threads").length() > 0)
			sbuf.append(" -threads " + encodingOptions.get("threads"));
		//always overwrite
		sbuf.append(" -y");
		
		//Performance question: are a string parsing and 4 subsequent int comparisons cheaper than 4 string.equals comparisons? 
		if (nMode == 1) {
			//SINGLE PASS
			if (encodingOptions.get("abitrate").length() > 0)
				sbuf.append(" -ab " + encodingOptions.get("abitrate"));
			if (encodingOptions.get("achannels").length() > 0)
				sbuf.append(" -ac " + encodingOptions.get("achannels"));
		} else if (nMode == 2) {

		} else if (nMode == 3) {
			sbuf.append(" -an -pass 1");
		
		} else if (nMode == 4) {
			if (encodingOptions.get("abitrate").length() > 0)
				sbuf.append(" -ab " + encodingOptions.get("abitrate"));
			if (encodingOptions.get("achannels").length() > 0)
				sbuf.append(" -ac " + encodingOptions.get("achannels"));
			sbuf.append(" -pass 2");
		} else {
			System.err.println("error creating ffmpeg exec command\n"+sbuf.toString());
		}
		
		sbuf.append(" @OUTPUTFILE@ ");
//		System.out.println("DEBUG: "+sbuf.toString());
		return sbuf.toString();
	}
	
	private String getBinary(String sBinary) {
		if (System.getProperty("os.name").startsWith("Windows")) {
			return mWinBinaries.get(sBinary);
		} else if (System.getProperty("os.name").startsWith("Mac")) {
			return mMacBinaries.get(sBinary);
		} else {
			System.err.println("unsupported OS..."
					+ System.getProperty("os.name"));
		}
		return null;
	}
	
	public void fileInfoResult(String sFileName, HashMap<String, String> mFileInfo) {
		mVideoInfo.put(sFileName, mFileInfo);
	}

	public void getInputFileInfo(List<String> sInputFileList) {
		if (vipv == null)
			vipv = new IPodEncoderVideoInfoProgressView(iPodEncoderView);
		
		vipv.ui.progressBar.setMaximum(sInputFileList.size());
		vipv.show();
		for (int cnt=0; cnt< sInputFileList.size(); ++cnt) {
			if (!mVideoInfo.containsKey(sInputFileList.get(cnt))) {
				MPlayerOutputParserTask mop = new MPlayerOutputParserTask();

				mop.setFile(sInputFileList.get(cnt));
				mop.publish.connect(this, "fileInfoResult(String, HashMap)");
				
				QExecuteTask executor = new QExecuteTask(mop);
				executor.setExecCommand(mplayerCommandSource.replace("@MPLAYER_BINARY@", getBinary("@MPLAYER_BINARY@")), sInputFileList.get(cnt));
				vipv.ui.progressBar.setValue(cnt+1);
				QThread qExec = new QThread(executor);
				qExec.start();
				try {
					qExec.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		vipv.dispose();
		vipv = null;
	}
	
	public void encode(ArrayList<String> lInputFiles, int nMode) {		
		ArrayList<FFMpegEncodeOptions> encodeOptions = new ArrayList<FFMpegEncodeOptions>();
		
		if (lInputFiles.size() == 0) {
			QMessageBox.information(iPodEncoderView, "No input", "Please provide input files.", QMessageBox.StandardButton.Ok);
			return;
		}
		
		if (fepv == null)
			fepv = new IPodEncoderFFMpegEncodingProgressView(iPodEncoderView);
		
		for (String sFileName : lInputFiles) {			
			if (!iPodEncoderView.ui.checkBox_overwrite.isChecked()) {
				File outputFile = new File(sFileName+".mp4");
				if (outputFile.exists()) {
					if (QMessageBox.question(iPodEncoderView, "File exists", "The output file " + outputFile.getName() + " already exists.\nDo you want to continue, overwriting it?", QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No) == StandardButton.No.value()) {
						return;
					}
				}
			}
			
			FFMpegEncodeOptions ffmeo = new FFMpegEncodeOptions();
			ffmeo.inputfilename = sFileName;
			ffmeo.outputfilename  = sFileName + ".mp4";
			ffmeo.encodemode = nMode;
			if (nMode == 2) { // two-pass
				FFMpegEncodeOptions ffmeo_pass1 = new FFMpegEncodeOptions();
				ffmeo_pass1.inputfilename = ffmeo.inputfilename;
				ffmeo_pass1.outputfilename = ffmeo.outputfilename;
				ffmeo_pass1.encodemode = 3;
				ffmeo_pass1.encodecommand = buildFFMpegExecCommand(sFileName, ffmeo_pass1.encodemode);
				encodeOptions.add(ffmeo_pass1);
				ffmeo.encodemode = 4;
			}
			ffmeo.encodecommand = buildFFMpegExecCommand(sFileName, ffmeo.encodemode);
			encodeOptions.add(ffmeo);
		}
		

		fepv.show();

		FFMpegOutputParserTask ffmpeg = new FFMpegOutputParserTask();
		ffmpeg.initialize(mVideoInfo);
		ffmpeg.publishStatus.connect(fepv, "updateStatus(FFMpegEncodeStatus)");
//		ffmpeg.encodingDone.connect(this, "encodingDone()");
			
		QExecuteTaskEx executor = new QExecuteTaskEx(ffmpeg);
		executor.setEncodingOptions(encodeOptions);
		
		QThread qExec = new QThread(executor);
		qExec.finished.connect(this, "encodingDone()");
		qExec.start();		
	}
	
	public void encodingDone() {
		fepv.dispose();
		fepv = null;
	}
}
